package cn.lg.soar.mvc.handler;

import cn.lg.soar.common.exceptions.AuthenticationException;
import cn.lg.soar.common.exceptions.PermissionException;
import cn.lg.soar.common.model.HttpResult;
import cn.lg.soar.common.model.TokenInfo;
import cn.lg.soar.common.util.IpUtil;
import cn.lg.soar.common.util.current.CurrentUser;
import cn.lg.soar.common.util.current.ThreadLocalHolder;
import cn.lg.soar.common.util.data.StringUtil;
import cn.lg.soar.core.manager.BaseAuthenticationManager;
import cn.lg.soar.core.manager.PermitLevelManager;
import cn.lg.soar.core.manager.TokenManager;
import cn.lg.soar.core.util.InnerSerializeUtils;
import cn.lg.soar.mvc.config.SoarResponseWrapper;
import cn.lg.soar.mvc.util.ResponseUtils;
import cn.lg.soar.mvc.util.ServletUtils;
import com.alibaba.fastjson.JSONObject;
import org.springframework.http.HttpStatus;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.function.Function;

/**
 * 权限过滤器
 * @author luguoxiang
 * @date 2022/8/25
 * 开源项目：https://gitee.com/lgx1992/lg-soar 求star！请给我star！请帮我点个star！
 */
public class AuthFilter implements Filter {

    // 登录接口权限级别
    private final static int LOGIN = 0;
    // 匿名访问级别
    private final static int ANON = 1;
    // 登录后访问级别
    private final static int AUTH = 2;
    // 验证用户类型的级别基本
    private final static int AUTH_BY_TYPE_BEGIN = 3;

    // 权限级别管理器
    private final PermitLevelManager permitLevelManager;
    // token管理器
    private final TokenManager tokenManager;
    // 权限验证管理器
    private final BaseAuthenticationManager<Long> authenticationManager;
    // 是否是超级管理员
    private final Function<String, Boolean> isSuperAdministrator;

    public AuthFilter(PermitLevelManager permitLevelManager, TokenManager tokenManager, BaseAuthenticationManager<Long> authenticationManager, Function<String, Boolean> isSuperAdministrator) {
        this.permitLevelManager = permitLevelManager;
        this.tokenManager = tokenManager;
        this.authenticationManager = authenticationManager;
        this.isSuperAdministrator = isSuperAdministrator;
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        HttpServletResponse response = (HttpServletResponse) servletResponse;
        String permit = request.getMethod().toUpperCase() + request.getServletPath();
        // 1.设置用户真实ip
        String remoteIp = IpUtil.getRemoteIp(request);
        // 获取权限级别
        int permitLevel = permitLevelManager.match(permit);

        // 验证token
        CurrentUser currentUser;
        String token = ServletUtils.getHeaderOrParameter(request, "token");
        if (token == null) {
            currentUser = null;
        } else {
            currentUser = tokenManager.getUserInfo(token, remoteIp);
        }

        // 是否是登录请求
        if (permitLevel == LOGIN) {
            if (currentUser != null) {
                // 保存用户信息
                ThreadLocalHolder.setCurrentUser(currentUser);
            }
            // 是登录请求的根据刷新token生成访问token
            response.setCharacterEncoding(StringUtil.CHARSET);
            SoarResponseWrapper soarResponse = new SoarResponseWrapper(response);
            filterChain.doFilter(servletRequest, soarResponse);
            // 读取结果生成token
            String json = new String(soarResponse.toByteArrayAndClear(), response.getCharacterEncoding());
            JSONObject jsonObject = InnerSerializeUtils.parseObject(json);
            if (Boolean.TRUE.equals(jsonObject.get("success")) &&
                    !StringUtil.isBlank(jsonObject.getString("data"))) {
                // 请求成功的生成访问token
                String refreshToken = jsonObject.getString("data");
                String accessToken = tokenManager.create(refreshToken, remoteIp);
                TokenInfo tokenInfo = new TokenInfo();
                tokenInfo.setRefreshToken(refreshToken);
                tokenInfo.setAccessToken(accessToken);
                jsonObject.put("data", tokenInfo);
                // 写入新的响应内容
                ResponseUtils.responseBody(response, jsonObject.toJSONString());
            } else {
                // 回写原来的响应内容
                ResponseUtils.responseBody(response, json);
            }
            return;
        }

        // token是否有效
        if (currentUser == null) {
            // token无效的请求（未登录）
            if (permitLevel == ANON) {
                filterChain.doFilter(servletRequest, servletResponse);
                return;
            }
            // token无效，需先登录
            response.setStatus(HttpStatus.UNAUTHORIZED.value());
            ResponseUtils.responseBody(response, HttpResult.fail(new AuthenticationException()));
            return;
        }

        // 保存用户信息
        ThreadLocalHolder.setCurrentUser(currentUser);

        // 是否不需要指定权限
        if (permitLevel == ANON || permitLevel == AUTH) {
            filterChain.doFilter(servletRequest, servletResponse);
            return;
        }

        // 是否是指定用户类型可访问的路径
        Integer userType = currentUser.getUserType();
        if (userType.equals(permitLevel - AUTH_BY_TYPE_BEGIN)) {
            filterChain.doFilter(servletRequest, servletResponse);
            return;
        }

        // 是否拥有指定权限
        if (authenticationManager.hasPermit(currentUser.getId(), permit)) {
            filterChain.doFilter(servletRequest, servletResponse);
            return;
        }

        // 是否是超级管理员
        if (Boolean.TRUE.equals(isSuperAdministrator.apply(currentUser.getUsername()))) {
            filterChain.doFilter(servletRequest, servletResponse);
            return;
        }

        // 无权限
        response.setStatus(HttpStatus.FORBIDDEN.value());
        ResponseUtils.responseBody(response, HttpResult.fail(new PermissionException()));
    }

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }
}
